//! 数据帧格式

use crate::consts::*;
use crc16;
use heapless::Vec;

/// 前导
pub(crate) const CHAR_LEAD: u8 = 0x55;
/// 起始符
pub(crate) const CHAR_START: u8 = 0x54;
/// 结束符
pub(crate) const CHAR_END: u8 = 0x0A;

/// 数据帧最小长度(负载长度为0)
pub const MIN_LEN: usize = (Field::Load as isize - Field::Check as isize) as usize;

/// 数据帧字段相对位置
pub(crate) enum Field {
    _Start = 0,
    Length = 1,
    Load = 3,
    Check = -3,
    _End = -1,
}

/// 将负载数据转化为数据帧
pub(crate) fn load2frame(load: &[u8]) -> Result<Vec<u8, FrameLen>, &'static str> {
    let load_len = load.len();
    if load_len > LOAD_LEN {
        return Err("Frame load too long.");
    }
    // 设置校验
    let check = crc16::State::<crc16::MODBUS>::calculate(load);

    let mut frame: Vec<u8, FrameLen> = Vec::from_slice(&[CHAR_LEAD; LEAD_LEN]).unwrap();
    frame.push(CHAR_START).unwrap();
    frame.extend_from_slice(&[(load_len >> 8) as u8, load_len as u8]).unwrap();
    frame.extend_from_slice(load).unwrap();
    frame.extend_from_slice(&[(check >> 8) as u8, check as u8]).unwrap();
    frame.push(CHAR_END).unwrap();
    return Ok(frame);
}

/// 将数据帧转化为负载数据
pub(crate) fn frame2load(frame: &[u8], load: &mut [u8]) -> usize {
    let frame_len = frame.len();
    if frame_len < MIN_LEN || frame_len > FRAME_LEN {
        return 0;
    }
    let mut i_start = 0;
    while frame[i_start] == CHAR_LEAD {
        i_start += 1;
    }
    let frame_load_len = (frame[i_start + Field::Length as usize] as usize) << 8
        | frame[i_start + Field::Length as usize + 1] as usize;
    let frame_load =
        &frame[i_start + Field::Load as usize..i_start + Field::Load as usize + frame_load_len];
    let frame_check = (frame[i_start + Field::Load as usize + frame_load_len] as u16) << 8
        | frame[i_start + Field::Load as usize + frame_load_len + 1] as u16;
    // CRC检验
    let check = crc16::State::<crc16::MODBUS>::calculate(frame_load);
    if check == frame_check {
        load[..frame_load_len].copy_from_slice(frame_load);
        return frame_load_len;
    } else {
        return 0;
    }
}
